from flask import Flask, request, render_template, send_file, make_response
from flask_socketio import SocketIO, emit
import os
import logging
import shutil
from tqdm import tqdm
import subprocess
from logging.handlers import MemoryHandler
from queue import Queue
import threading
import eventlet
eventlet.monkey_patch()

# 获取当前登录用户名
def get_current_user():
    logged_in_user = request.cookies.get('logged_in_user', 'unknown')
    return f"{logged_in_user} "

# 自定义日志Formatter，添加用户名
class CustomFormatter(logging.Formatter):
    def format(self, record):
        # 在每条日志前添加用户名
        record.msg = f"{get_current_user()}{record.msg}"
        return super().format(record)

# 添加日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 为所有handler设置自定义Formatter
for handler in logging.getLogger().handlers:
    handler.setFormatter(CustomFormatter('%(asctime)s - %(levelname)s - %(message)s'))


# 配置目录路径
ANDROID_PROJECT_PATH = "D:/Project/ShuZhiYuanProject/shuzhiyuan_android_external_pkg"

CHAT_ASSETS_DEBUG_PATH = os.path.join(ANDROID_PROJECT_PATH, "kunLunFuleCard/src/main/assets_backup/chat_debug")
CARLIFE_ASSETS_DEBUG_PATH = os.path.join(ANDROID_PROJECT_PATH, "kunLunFuleCard/src/main/assets_backup/carlife_debug")

CHAT_ASSETS_RELEASE_PATH = os.path.join(ANDROID_PROJECT_PATH, "kunLunFuleCard/src/main/assets_backup/chat_release")
CARLIFE_ASSETS_RELEASE_PATH = os.path.join(ANDROID_PROJECT_PATH, "kunLunFuleCard/src/main/assets_backup/carlife_release")

KUNLUN_MAIN_PATH = os.path.join(ANDROID_PROJECT_PATH, "kunLunFuleCard/src/main")

APK_EXTERNAL_DEBUG_PATH = os.path.join(ANDROID_PROJECT_PATH, "kunLunFuleCard/build/outputs/apk/external/debug")
APK_EXTERNAL_RELEASE_PATH = os.path.join(ANDROID_PROJECT_PATH, "kunLunFuleCard/build/outputs/apk/external/release")

# CHAT_ASSETS_RELEASE_PATH = "D:/Project/ShuZhiYuanProject/ApkBuild/file-upload-server/server/uploads/chat"
# CARLIFE_ASSETS_RELEASE_PATH = "D:/Project/ShuZhiYuanProject/ApkBuild/file-upload-server/server/uploads/carlife"

app = Flask(__name__)
socketio = SocketIO(app)

# 创建一个队列来存储日志消息
log_queue = Queue()

# 自定义的日志处理器，将日志消息发送到WebSocket
class WebSocketHandler(logging.Handler):
    def emit(self, record):
        log_queue.put(self.format(record))

# 配置WebSocket日志处理器
websocket_handler = WebSocketHandler()
websocket_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logging.getLogger().addHandler(websocket_handler)

# 后台线程，用于发送日志消息
def send_log_messages():
    while True:
        if not log_queue.empty():
            message = log_queue.get()
            socketio.emit('log_message', {'data': message})
        socketio.sleep(1)

# 启动后台线程
thread = threading.Thread(target=send_log_messages)
thread.daemon = True
thread.start()

# 允许登录的用户列表
ALLOWED_USERS = ["许文会", "韩野", "薛丽", "王奥", "朱晓曦", "雷克", "赵亚泽", "贾胜军", "师文昆"]
VALID_PASSWORD = "123"

@app.route('/')
def index():
    logged_in_user = request.cookies.get('logged_in_user')
    if logged_in_user in ALLOWED_USERS:
        return render_template('index.html', logged_in=True)
    return render_template('index.html', logged_in=False)

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    if username in ALLOWED_USERS and password == VALID_PASSWORD:
        resp = make_response({"message": "Login successful"}, 200)
        # 设置cookie，max_age为3天（3 * 24 * 60 * 60秒）
        resp.set_cookie('logged_in_user', username, max_age=3*24*60*60, httponly=True)
        return resp
    else:
        return {"error": "Invalid username or password"}, 401

@app.route('/upload', methods=['POST'])
def upload_package():
    files = request.files.getlist('files')
    type = request.form.get('type')
    logging.info(f"接收到上传请求, type: {type}, 文件数量: {len(files)}")
    
    # 验证请求参数
    if not files:
        return {'error': 'No files provided'}, 422
    if not type:
        return {'error': 'Type parameter is required'}, 422
    
    # 解析type参数
    try:
        package_type, build_type = type.split('-')
        if package_type not in ["chat", "carlife"]:
            return {'error': 'Invalid package type. Must be \'chat\' or \'carlife\''}, 422
        if build_type not in ["debug", "release"]:
            return {'error': 'Invalid build type. Must be \'debug\' or \'release\''}, 422
    except ValueError:
        return {'error': 'Invalid type format. Must be \'package-buildtype\''}, 422
        
    try:
        target_dir = ''
        if(package_type == 'chat'):
            if(build_type == 'debug') :
                target_dir = CHAT_ASSETS_DEBUG_PATH
            else:
                target_dir = CHAT_ASSETS_RELEASE_PATH
        else:
            if(build_type == 'debug') :
                target_dir = CARLIFE_ASSETS_DEBUG_PATH
            else:
                target_dir = CARLIFE_ASSETS_RELEASE_PATH
        # 确定目标目录
        logging.info(f"开始上传 {type} 包到 {target_dir}")
        
        # 确保目录存在
        os.makedirs(target_dir, exist_ok=True)
        
        # 清空目标目录
        logging.info("清理目标目录, target_dir = " + target_dir)
        for item in os.listdir(target_dir):
            item_path = os.path.join(target_dir, item)
            if os.path.isfile(item_path):
                os.remove(item_path)
            elif os.path.isdir(item_path):
                shutil.rmtree(item_path)
        
        # 保存上传的文件，保持文件夹结构
        total_files = len(files)
        logging.info(f"开始处理 {total_files} 个文件")
        for index, file in enumerate(files, 1):
            if not file.filename:
                logging.warning(f"跳过空文件: {file}")
                continue
            # 获取文件名，只取第一个/后的内容
            relative_path = file.filename.split('/', 1)[1] if '/' in file.filename else file.filename
            # logging.info(f"相对路径: {relative_path}")
            if not relative_path:
                continue
                
            target_path = os.path.join(target_dir, relative_path)
            # logging.info(f"处理文件 ({index}/{total_files})  target_path: {target_path}")
            
            # 确保目标文件的父目录存在
            os.makedirs(os.path.dirname(target_path), exist_ok=True)
            
            # 获取文件大小并保存
            # logging.info(f"开始保存文件, target_path = {target_path}")
            file.save(target_path)
            
            # logging.info(f"文件保存成功, target_path = : {target_path}")
        
        logging.info(f"{type} 包上传完成")
        return {"message": f"{type} package uploaded successfully", "total_files": total_files}
    except Exception as e:
        error_msg = f"上传失败: {str(e)}"
        logging.error(error_msg)
        return {'error': error_msg}, 500

@app.route('/build', methods=['POST'])
def build_apk():
    logging.info("开始执行 build_apk(request) 构建APK")
    try:
        data = request.get_json()
        build_type = data.get('buildType', 'debug')
        
        # 执行process_pkg.py
        process_pkg_abs_path = os.path.join(KUNLUN_MAIN_PATH, "process_pkg.py")
        subprocess.run(["python", process_pkg_abs_path, KUNLUN_MAIN_PATH, build_type], check=True)
        logging.info(f"process_pkg.py 执行成功, buildType: {build_type}")

        # 执行gradle clean
        gradlew_path = os.path.join(ANDROID_PROJECT_PATH, "gradlew.bat")
        logging.info("开始执行 gradle clean")
        subprocess.run([gradlew_path, "clean"], cwd=ANDROID_PROJECT_PATH, check=True)
        logging.info("gradle clean 执行成功")

        # 执行构建命令
        gradle_cmd = "assembleDebug" if build_type == "debug" else "assembleRelease"
        logging.info(f"开始构建APK, gradle_cmd: {gradle_cmd}")
        subprocess.run([gradlew_path, gradle_cmd], cwd=ANDROID_PROJECT_PATH, check=True)
        logging.info("APK构建成功")
        
        return {"message": "APK built successfully"}
    except subprocess.CalledProcessError as e:
        error_msg = f"构建失败: {str(e)}"
        logging.error(error_msg)
        return {'error': error_msg}, 500
    except Exception as e:
        error_msg = f"构建过程发生错误: {str(e)}"
        logging.error(error_msg)
        return {'error': error_msg}, 500

@app.route('/download', methods=['GET'])
def download_apk():
    logging.info("开始执行 download_apk() 下载APK")
    try:
        build_type = request.args.get('buildType', 'debug')
        
        # 根据build_type选择目录路径
        apk_dir = APK_EXTERNAL_DEBUG_PATH if build_type == 'debug' else APK_EXTERNAL_RELEASE_PATH
        
        # 获取目录中第一个.apk文件
        apk_filename = next((f for f in os.listdir(apk_dir) if f.endswith('.apk')), None)
        if not apk_filename:
            return {'error': 'APK file not found'}, 404
            
        apk_path = os.path.join(apk_dir, apk_filename)
        if not os.path.exists(apk_path):
            return {'error': 'APK file not found'}, 404
        
        logging.info("apk_path = " + apk_path + " apk_filename = " + apk_filename)
        return send_file(apk_path, as_attachment=True, download_name=apk_filename)
    except Exception as e:
        logging.error(f"下载APK失败: {str(e)}")
        return {'error': str(e)}, 500

# 操作锁
current_operation = None
operation_lock = threading.Lock()

def acquire_operation_lock(username, operation):
    """获取操作锁"""
    global current_operation
    with operation_lock:
        if current_operation:
            return False, current_operation
        current_operation = {'user': username, 'operation': operation}
        return True, None

def release_operation_lock(username):
    """释放操作锁"""
    global current_operation
    with operation_lock:
        if current_operation and current_operation['user'] == username:
            current_operation = None
            return True
    return False

@app.route('/check_operation', methods=['GET'])
def check_operation():
    """检查当前是否有操作正在进行"""
    with operation_lock:
        if current_operation:
            return {'busy': True, 'user': current_operation['user'], 'operation': current_operation['operation']}
        return {'busy': False}

@app.route('/acquire_lock', methods=['POST'])
def acquire_lock():
    """尝试获取操作锁"""
    data = request.get_json()
    username = request.cookies.get('logged_in_user')
    operation = data.get('operation')
    
    success, current_user = acquire_operation_lock(username, operation)
    if success:
        return {'success': True}
    return {'success': False, 'current_user': current_user['user'], 'operation': current_user['operation']}

@app.route('/release_lock', methods=['POST'])
def release_lock():
    """释放操作锁"""
    username = request.cookies.get('logged_in_user')
    if release_operation_lock(username):
        return {'success': True}
    return {'success': False}

if __name__ == "__main__":
    socketio.run(app, host='0.0.0.0', port=8082, debug=True)